/*
*
==============================================================================
*  Name        : 
*  Part of     : Omen Game Engine
*  Description : 
*  Version     : 1.0
*
*  Copyright (c) 2013 Lauri Kortevaara.
* 
==============================================================================
*/

#define OMEN_SOURCE 1

#include <IOmenFrustum.h>


using namespace Omen;

Frustum::Frustum()
{
}

Frustum::~Frustum()
{
}
            
void Frustum::create( Omen::Matrix4x4& viewMatrix, Omen::Matrix4x4& projectionMatrix, Omen::Float depth )
{
    float zMinimum, r;
    Omen::Matrix4x4 matrix;

    
    // Calculate the minimum Z distance in the frustum.
    zMinimum = -projectionMatrix.m43 / projectionMatrix.m33;
    r = depth / (depth - zMinimum);
    projectionMatrix.m33 = r;
    projectionMatrix.m43 = -r * zMinimum;

    // Create the frustum matrix from the view matrix and updated projection matrix.
    matrix = viewMatrix * projectionMatrix;

    // Near plane
    m_planes[0].a = matrix.m14 + matrix.m13;
    m_planes[0].b = matrix.m24 + matrix.m23;
    m_planes[0].c = matrix.m34 + matrix.m33;
    m_planes[0].d = matrix.m44 + matrix.m43;
    m_planes[0].normalize();

    // Far plane
    m_planes[1].a = matrix.m14 - matrix.m13; 
    m_planes[1].b = matrix.m24 - matrix.m23;
    m_planes[1].c = matrix.m34 - matrix.m33;
    m_planes[1].d = matrix.m44 - matrix.m43;
    m_planes[1].normalize();

    // Left plane
    m_planes[2].a = matrix.m14 + matrix.m11; 
    m_planes[2].b = matrix.m24 + matrix.m21;
    m_planes[2].c = matrix.m34 + matrix.m31;
    m_planes[2].d = matrix.m44 + matrix.m41;
    m_planes[2].normalize();

    // Right plane
    m_planes[3].a = matrix.m14 - matrix.m11; 
    m_planes[3].b = matrix.m24 - matrix.m21;
    m_planes[3].c = matrix.m34 - matrix.m31;
    m_planes[3].d = matrix.m44 - matrix.m41;
    m_planes[3].normalize();

    // Top plane
    m_planes[4].a = matrix.m14 - matrix.m12; 
    m_planes[4].b = matrix.m24 - matrix.m22;
    m_planes[4].c = matrix.m34 - matrix.m32;
    m_planes[4].d = matrix.m44 - matrix.m42;
    m_planes[4].normalize();

    // Bottom plane
    m_planes[5].a = matrix.m14 + matrix.m12;
    m_planes[5].b = matrix.m24 + matrix.m22;
    m_planes[5].c = matrix.m34 + matrix.m32;
    m_planes[5].d = matrix.m44 + matrix.m42;
    m_planes[5].normalize();
}

bool Frustum::pointInside( Omen::Vector3d& pointCoord )
{
    bool bInside = true;
    // The point is inside the frustum if DotProduct(Plane,Point) is positive for all planes
    for( int i=0; i<6; ++i) 
    {
        if( m_planes[i].dotProduct(pointCoord) < 0.0f)
        {
            bInside = false;
            break;
        }
    }
    return bInside;
}

bool Frustum::sphereInside( Omen::Sphere& sphere )
{
    bool bInside = true;
    // The sphere is inside the frustum if DotProduct(Plane,SphereCenter) is less than radius of sphere for all planes
    for(int i=0; i<6; ++i) 
    {
        if(m_planes[i].dotProduct( sphere.position() ) < -sphere.radius() )
        {
            bInside = false;
            break;
        }
    }
    return bInside;
}

bool Frustum::AABBInside( Omen::AABB& aabb )
{
    bool bInside = true;
    
    for( int i=0; i < 6; ++i )
    {
        Omen::Vector3d positive(m_planes[i].a > 0 ? aabb.maximum().x : aabb.minimum().x,
                                m_planes[i].b > 0 ? aabb.maximum().y : aabb.minimum().y,
                                m_planes[i].c > 0 ? aabb.maximum().z : aabb.minimum().z);

        Omen::Vector3d negative(m_planes[i].a < 0 ? aabb.maximum().x : aabb.minimum().x,
                                m_planes[i].b < 0 ? aabb.maximum().y : aabb.minimum().y,
                                m_planes[i].c < 0 ? aabb.maximum().z : aabb.minimum().z);

        Omen::Float n = m_planes[i].dotProduct( positive );
        if( m_planes[i].dotProduct( positive ) < 0.0 || m_planes[i].dotProduct( negative ) < 0.0 )
        {
            bInside = false;
            break;
        }
    }
    return bInside;
}

bool Frustum::boxInside( Omen::Box& box )
{
    bool bInside = false;
    return bInside;
}
